package sms

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"product/common/encryption"
	"product/common/validator"
	"strconv"
	"strings"
)

type Bao struct {
	BaseSms
	// 密码或key
	Password string
}

func NewSmsBao(userName, password, appKey, apiUrl string) *Bao {
	return &Bao{
		BaseSms: BaseSms{
			ApiUrl:   apiUrl,
			AppKey:   appKey,
			UserName: userName,
		},
		Password: password,
	}
}

func (s *Bao) SendSms(smsContent, mobile string) (bool, error) {
	// 密码不为空则作为key赋予
	if !validator.IsEmptyString(s.Password) {
		s.AppKey = s.Password
	}
	ok, err := s.CheckInputValid(smsContent, mobile)
	if !ok {
		return false, err
	}

	reqParams := s.buildParams(smsContent, mobile)
	strUrl := fmt.Sprintf("%s%s", s.ApiUrl, reqParams)
	status, err := httpGetHandler(strUrl)
	if err != nil {
		return false, err
	}
	return httpStatusHandler(status)
}

// BatchSendSms 批量发送短信
func (s *Bao) BatchSendSms(smsContent string, mobiles []string) (bool, error) {
	valid, err := s.CheckBatchInputValid(smsContent, mobiles)
	if !valid {
		return false, err
	}
	reqParams := s.buildParams(smsContent, strings.Join(mobiles, ","))
	strUrl := fmt.Sprintf("%s%s", s.ApiUrl, reqParams)
	status, err := httpGetHandler(strUrl)
	if err != nil {
		return false, err
	}
	return httpStatusHandler(status)
}

// ##### 私有方法 #####
// buildParams 构建请求参数
func (s *Bao) buildParams(smsContent string, mobile string) string {
	strParam := strings.Builder{}
	encodeContent := url.QueryEscape(smsContent)
	var strKey string
	if !validator.IsEmptyString(s.AppKey) {
		strKey = s.AppKey
	} else {
		strKey = encryption.Md5(s.Password)
	}
	strParam.WriteString(fmt.Sprintf("?u=%s&p=%s&m=%s&c=%s", s.UserName, strKey, mobile, encodeContent))
	return strParam.String()
}

// httpStatusHandler 请求错误处理
func httpStatusHandler(status BaoProcessStatus) (bool, error) {
	errDesc := status.ToBaoDescription()
	if status == BaoSuccess {
		return true, nil
	}
	return false, errors.New(errDesc)
}

// httpGetHandler 请求处理
func httpGetHandler(url string) (BaoProcessStatus, error) {
	rest, err := http.Get(url)
	if err != nil {
		return BaoProcessStatus(-1), err
	}
	defer func(Body io.ReadCloser) {
		_ = Body.Close()
	}(rest.Body)
	response, _ := io.ReadAll(rest.Body)
	status, err := strconv.Atoi(string(response))
	return BaoProcessStatus(status), err
}

// ##### 私有方法end #####
